#!/usr/bin/python
# -*- coding: utf8 -*-

from pyparsing import Keyword

from expresion import simpleTypeFactory, atomoFactory
from expresion import Expresion, TypeException, Exp, makeSintaxis
from lenguaje import Extension


# true y false son valores, "reducen" a si mismos y tipan por defecto:
# vacio | > true : Boolean
# vacio | > flase : Boolean
Boolean = simpleTypeFactory('Boolean')
Verdadero = atomoFactory('true', Boolean)
Falso = atomoFactory('false', Boolean)
    
class IfThenElse(Expresion):
    
    def __init__(self, toks):
        """
        Define para la estructura IfThenElse la guarda y las
        ramas por verdadero y por falso.
        """
        guarda, ramaTrue, ramaFalse = toks
        self.guarda = guarda
        self.ramaTrue = ramaTrue
        self.ramaFalse = ramaFalse
        
    def reducir(self):
        """
        Reducción de la guarda a un valor. Luego dependiendo de dicho valor
        se reduce la rama verdadera o la falsa.
        """
        if isinstance(self.guarda.reducir(), Verdadero):
            return self.ramaTrue.reducir()
        else:
            return self.ramaFalse.reducir()
            
   
    def tipar(self, namespace):
        """
        gamma | > Guarda : Boolean, gamma | > RamaTrue : alpha, gamma | > RamaFalse : alpha
        ---------------------------------------------------------------
        gamma | > if Guarda then RamaTrue else RamaFalse : alpha
        """
        tipoGuarda = self.guarda.tipar(namespace)
        tipoRamaTrue = self.ramaTrue.tipar(namespace)
        tipoRamaFalse = self.ramaFalse.tipar(namespace)
        # gamma | > Guarda : Boolean
        if  tipoGuarda == Boolean([]):
            # gamma | > RamaTrue : alpha, gamma | > RamaFalse : alpha
            if  tipoRamaTrue == tipoRamaFalse:
                # gamma | > if Guarda then RamaTrue else RamaFalse : alpha
                return tipoRamaTrue
            else:
                raise TypeException('Tipo rama true: %s \n' % tipoRamaTrue + \
                                    'Tipo rama false: %s \n' % tipoRamaFalse + \
                                    'No existe unificación entre los tipos de las ramas.')
        else:
            raise TypeException('Tipo de la guarda: %s \n' % tipoGuarda + \
                                'El tipo de la guarda no unifica con Boolean.')
                                
                
    
    def sustituir(self, var, expresion):
        """
        if M then N else O {X <- T} --> if M {X <- T} then N {X <- T} else O {X <- T}.
        """
        guarda = self.guarda.sustituir(var, expresion)
        ramaTrue = self.ramaTrue.sustituir(var, expresion)
        ramaFalse = self.ramaFalse.sustituir(var, expresion)
        return IfThenElse([guarda, ramaTrue, ramaFalse])
    
    @classmethod
    def sintaxis(cls):
        # if M then N else O
        If = makeSintaxis('if' , Exp,'then', Exp,'else', Exp)
        return If
    
    def __str__(self):
        return "if %s then %s else %s" % (self.guarda, self.ramaTrue, self.ramaFalse)


extensionBoolean = Extension('Booleanos', 
                             expresiones = [Verdadero, Falso, IfThenElse],
                             tipos = [Boolean]
                            )
